package com.springboot.util;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.springboot.annotation.WinBusiLogFormat;
import com.springboot.enumration.FormatEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 包名称：com.win.dfbp.inquiry.common.utils.ObjectCompareUtils
 * 类名称：ObjectCompareUtils
 * 类描述：对象比较工具类
 * 创建人：@author tanglei
 * 创建时间：2021/09/07 15:38
 */
@Slf4j
public class ObjectCompareUtils {

    private static final String COMPARE_MSG_TEMPLATE = "修改【%s】，%s修改为%s";


    /**
     * 部分忽略比较的字段
     */
    private static final String[] IGNORE_FIELDS = {
            "id",
            "createUserId",
            "createTime",
            "updateUserId",
            "updateTime",
            "serialVersionUID"
    };

    /**
     * @description:  获取默认的忽略字段
     * @param
     * @return: java.util.List<java.lang.String>
     * @author: tanglei
     * @time: 2022/5/17 14:20
     */
    private static List<String> getDafultIgnoreFields() {
        return new ArrayList<>(Arrays.asList(ObjectCompareUtils.IGNORE_FIELDS));
    }

    /**
     * @description:  对比全部字段
     * @param oldObject
     * @param newObject
     * @param clazz
     * @return: java.lang.String
     * @author: tanglei
     * @time: 2022/5/17 14:48
     */
    public static <T> String getCompareBusiMsgByAllFields(T oldObject, T newObject, Class<T> clazz) {

        List<String> compareResult = doCompare(Arrays.asList(clazz.getDeclaredFields()), oldObject, newObject);

        return compareResult.stream().collect(Collectors.joining("；"));
    }


    /**
     * @description:  指定需要对比的字段
     * @param oldObject
     * @param newObject
     * @param clazz
     * @param fields
     * @return: java.lang.String
     * @author: tanglei
     * @time: 2022/5/17 14:39
     */
    public static <T> String getCompareBusiMsgByFields(T oldObject, T newObject, Class<T> clazz, List<String> fields) {

        if (ObjectUtil.isNull(oldObject) || ObjectUtil.isNull(newObject)) {
            return null;
        }

        List<Field> needCompareFieldList = filterByFields(clazz, fields);

        List<String> compareResult = doCompare(needCompareFieldList, oldObject, newObject);

        return compareResult.stream().collect(Collectors.joining("；"));

    }

    /**
     * @description: 依据注解获取修改前后业务日志
     * @param oldObject 修改前对象
     * @param newObject 修改后对象
     * @param clazz     对比反射对象
     * @return: java.lang.String
     * @author: tanglei
     * @time: 2022/5/12 10:26
     */
    public static <T> String getCompareBusiMsgByAnnotation(T oldObject, T newObject, Class<T> clazz) {

        if (ObjectUtil.isNull(oldObject) || ObjectUtil.isNull(newObject)) {
            return null;
        }
        // 获取类所有需要对比属性
        List<Field> busiLogCompareFieldList = getCompareFieldByAnnotation(clazz);
        if (CollUtil.isEmpty(busiLogCompareFieldList)) {
            return null;
        }
        List<String> compareResult = doCompare(busiLogCompareFieldList, oldObject, newObject);
        return compareResult.stream().collect(Collectors.joining("；"));

    }

    /**
     * @description:  依据默认忽略字段获取对比信息
     * @param oldObject
     * @param newObject
     * @param clazz
     * @return: java.lang.String
     * @author: tanglei
     * @time: 2022/5/17 14:27
     */
    public static <T> String getCompareBusiMsg(T oldObject, T newObject, Class<T> clazz) {

        if (ObjectUtil.isNull(oldObject) || ObjectUtil.isNull(newObject)) {
            return null;
        }

        List<Field> needCompareFieldList = filterByIgnoreFields(clazz, getDafultIgnoreFields());
        if (CollUtil.isEmpty(needCompareFieldList)) {
            return "";
        }

        List<String> compareResult = doCompare(needCompareFieldList, oldObject, newObject);
        return compareResult.stream().collect(Collectors.joining("；"));
    }

    /**
     * @description:  在默认忽略字段上追加忽略字段进行对比
     * @param oldObject
     * @param newObject
     * @param clazz
     * @param ignoreFields
     * @return: java.lang.String
     * @author: tanglei
     * @time: 2022/5/17 14:38
     */
    public static <T> String getCompareBusiMsgByIgnoreFields(T oldObject, T newObject, Class clazz, List<String> ignoreFields) {

        if (ObjectUtil.isNull(oldObject) || ObjectUtil.isNull(newObject)) {
            return null;
        }

        if (CollUtil.isEmpty(ignoreFields)) {
            throw new RuntimeException();
        }
        List<String> defaultIgnoreFields = getDafultIgnoreFields();
        defaultIgnoreFields.addAll(ignoreFields);

        List<Field> needCompareFieldList = filterByIgnoreFields(clazz, defaultIgnoreFields);
        if (CollUtil.isEmpty(needCompareFieldList)) {
            return "";
        }

        List<String> compareResult = doCompare(needCompareFieldList, oldObject, newObject);
        return compareResult.stream().collect(Collectors.joining("；"));

    }

    /**
     * @description: 依据注解获取需要对比的属性
     * @param clazz 对比反射对象
     * @return: java.util.List<java.lang.reflect.Field>
     * @author: tanglei
     * @time: 2022/5/12 10:27
     */
    private static List<Field> getCompareFieldByAnnotation(Class<?> clazz) {
        List<Field> result = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            WinBusiLogFormat annotation = field.getAnnotation(WinBusiLogFormat.class);
            if (ObjectUtil.isNotNull(annotation)) {
                result.add(field);
            }
        }
        return result;
    }

    /**
     * @description:  依据需要对比的字段做对比
     * @param needCompareFieldList
     * @param oldObject
     * @param newObject
     * @return: java.util.List<java.lang.String>
     * @author: tanglei
     * @time: 2022/5/17 13:15
     */
    private static <T> List<String> doCompare(List<Field> needCompareFieldList, T oldObject, T newObject) {

        List<String> result = new ArrayList<>();

        if (CollUtil.isEmpty(needCompareFieldList)) {
            return result;
        }

        //遍历需要对比字段
        for (Field field : needCompareFieldList) {
            if (ObjectUtil.isNotEmpty(field)) {
                field.setAccessible(true);
            }
            String compareMsg = getFieldCompareResult(field, oldObject, newObject);
            if (StrUtil.isBlank(compareMsg)) {
                continue;
            }
            result.add(compareMsg);
        }
        return result;
    }

    /**
     * @description: 根据忽略字段，获取需要对比的属性
     * @param clazz 对比反射对象
     * @return: java.util.List<java.lang.reflect.Field>
     * @author: tanglei
     * @time: 2022/5/12 10:27
     */
    private static List<Field> filterByIgnoreFields(Class<?> clazz, List<String> ignoreFieldList) {
        List<Field> result = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {

            boolean isIgnore = ignoreFieldList.contains(field.getName());
            if (isIgnore) {
                continue;
            }
            result.add(field);
        }
        return result;
    }

    /**
     * @description:  获取需要对比的属性，指定字段
     * @param clazz
     * @param fields
     * @return: java.util.List<java.lang.reflect.Field>
     * @author: tanglei
     * @time: 2022/5/17 13:46
     */
    private static List<Field> filterByFields(Class<?> clazz, List<String> fields) {
        List<Field> result = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            boolean isNeedCompare = fields.contains(field.getName());
            if (isNeedCompare) {
                result.add(field);
            }
        }
        return result;
    }

    /**
     * @description: 单个属性对比详情
     * @param comparefield 需要对比的属性
     * @param oldObject    修改前对象
     * @param newObject    修改后对象
     * @return: java.lang.String
     * @author: tanglei
     * @time: 2022/5/12 10:28
     */
    private static String getFieldCompareResult(Field comparefield, Object oldObject, Object newObject) {

        WinBusiLogFormat busiLogCompare = comparefield.getAnnotation(WinBusiLogFormat.class);
        ApiModelProperty apiModelProperty = comparefield.getAnnotation(ApiModelProperty.class);

        if (ObjectUtil.isEmpty(apiModelProperty)) {
            log.info("业务日志对比，无法获取字段{}上的ApiModelProperty注解", comparefield.getName());
            return "";
        }
        try {
            //转换字段对比
            if (ObjectUtil.isNotEmpty(busiLogCompare)) {

                //单位转换
                if (StrUtil.isNotBlank(busiLogCompare.unitConvert())
                        && StrUtil.equals(BigDecimal.class.getTypeName(), comparefield.getType().getTypeName())) {

                    BigDecimal oldDicimalValue = (BigDecimal) comparefield.get(oldObject);
                    BigDecimal newDicimalValue = (BigDecimal) comparefield.get(newObject);

                    String oldConvertValue = NumberUtil.div(oldDicimalValue, new BigDecimal(busiLogCompare.unitConvert()),
                            busiLogCompare.scale(), busiLogCompare.roundingMode()).toPlainString();

                    String newConvertValue = NumberUtil.div(newDicimalValue, new BigDecimal(busiLogCompare.unitConvert()),
                            busiLogCompare.scale(), busiLogCompare.roundingMode()).toPlainString();

                    if (StrUtil.equals(oldConvertValue, newConvertValue)) {
                        return "";
                    }
                    return String.format(COMPARE_MSG_TEMPLATE, apiModelProperty.value(), oldConvertValue, newConvertValue);
                }

                //字典转义
                if (!FormatEnum.EMPTY_ENUM.getType().equals(busiLogCompare.value().getType())) {

                    String oldValue = getValueByAnnotion(busiLogCompare, (String) comparefield.get(oldObject));
                    String newValue = getValueByAnnotion(busiLogCompare, (String) comparefield.get(newObject));

                    return getCompareMsg(oldValue, newValue, apiModelProperty.value());
                }
            }

            if (StrUtil.equals(BigDecimal.class.getTypeName(), comparefield.getType().getTypeName())) {

                BigDecimal oldDicimalValue = (BigDecimal) comparefield.get(oldObject);
                BigDecimal newDicimalValue = (BigDecimal) comparefield.get(newObject);

                if (ObjectUtil.isNotNull(busiLogCompare)) {
//                    return getCompareMsg(com.win.dfas.common.util.BigDecimalUtil.toPlainStringByScale(oldDicimalValue, busiLogCompare.scale(), busiLogCompare.roundingMode()),
//                            com.win.dfas.common.util.BigDecimalUtil.toPlainStringByScale(newDicimalValue, busiLogCompare.scale(), busiLogCompare.roundingMode()), apiModelProperty.value());
                }
                //默认 4位小数，四舍五入
                return "";

            }

            String oldValue = String.valueOf(comparefield.get(oldObject));
            String newValue = String.valueOf(comparefield.get(newObject));
            return getCompareMsg(oldValue, newValue, apiModelProperty.value());

        } catch (IllegalAccessException e) {
            log.error(ExceptionUtil.stacktraceToString(e));
//            throw WinExceptionUtil.winException(CommonExceptionEnum.FORMAT_EXCEPTION);
            throw new RuntimeException();
        }
    }


    /**
     * @description:  获取字典转义值（redis缓存值）
     * @param busiLogCompare
     * @param fieldValue
     * @return: java.lang.String
     * @author: tanglei
     * @time: 2022/5/17 13:17
     */
    private static String getValueByAnnotion(WinBusiLogFormat busiLogCompare, String fieldValue) {

        // 转义值
        StringBuffer valueBuffer = new StringBuffer();

        try {

            if (ObjectUtil.isNotNull(fieldValue)) {
                // 转换列表
                List<Object> separatorList = new ArrayList<Object>();

                // 分隔符处理
                if (StrUtil.isNotEmpty(busiLogCompare.separator())) {
                    separatorList.addAll(Arrays.asList(((String) fieldValue).split(busiLogCompare.separator())));
                } else {
                    separatorList.add(fieldValue);
                }

                // 参数对象处理
                Object[] paramObjects = new Object[busiLogCompare.prefixParam().length + 1];
                if (busiLogCompare.prefixParam().length > 0) {
                    System.arraycopy(busiLogCompare.prefixParam(), 0, paramObjects, 0, paramObjects.length - 1);
                }

                List<String> redisKeyList = new ArrayList<>();
                // 转换列表处理
                separatorList.forEach(item -> {
                    paramObjects[paramObjects.length - 1] = item;

                    // 获取注解枚举
                    FormatEnum formatEnum = busiLogCompare.value();

                    // 获取REDIS对应的值
                    String formatKey = formatEnum.getType() + formatEnum.getFormat();

                    String redisKey = String.format(formatKey, paramObjects);

                    redisKeyList.add(redisKey);
                });

                for (String redisKey : redisKeyList) {

//                    Object redisOjbect = RedisUtil.get(redisKey);
                    Object redisOjbect = new Object();

                    // 获取解析值
                    String valueKey = String.format(busiLogCompare.value().getKey());

                    if (StrUtil.isEmpty(valueKey)) {
                        valueKey = busiLogCompare.redisValueKey();
                    }

                    Map<String, Object> redisMap = JSON.parseObject(JSON.toJSONString(redisOjbect), Map.class);
                    if (ObjectUtil.isEmpty(redisMap)) {
                        return "";
                    }

                    // 兼容Oracle(Oracle列名自动转换为大写)
                    Object redisMapValue = redisMap.getOrDefault(valueKey, redisMap.get(valueKey.toUpperCase()));

                    // 处理分隔符
                    if (StrUtil.isNotEmpty(valueBuffer) && StrUtil.isNotEmpty(busiLogCompare.separator())) {
                        valueBuffer.append(busiLogCompare.separator());
                    }
                    valueBuffer.append(redisMapValue);
                }
                return valueBuffer.toString();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }

        return "";
    }

    /**
     * @param oldValue    旧值
     * @param newValue    新值
     * @param fieldChName 字段中文名
     * @description: 对比字段值获取对比信息
     * @return: java.lang.String
     * @author: tanglei
     * @time: 2022/5/13 21:03
     */
    private static String getCompareMsg(String oldValue, String newValue, String fieldChName) {
        if (StrUtil.equals(newValue, oldValue)) {
            return "";
        } else {
            return String.format(COMPARE_MSG_TEMPLATE, fieldChName, oldValue, newValue);
        }
    }


}
